using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace ZFramework
{
    public struct Vec3I : IEnumerable<int>, IEquatable<Vec3I>
    {
        public int x, y, z;

        public int this[int i]
        {
            readonly get => i switch
            {
                0 => x,
                1 => y,
                2 => z,
                _ => throw new IndexOutOfRangeException(),
            };
            set
            {
                switch(i)
                {
                    case 0:
                        x = value;
                        break;
                    case 1:
                        y = value;
                        break;
                    case 2:
                        z = value;
                        break;
                }
            }
        }
        public Vec3I(int x, int y, int z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        public Vec3I(IEnumerable<int> v)
        {
            var it = v.GetEnumerator();
            x = it.MoveNext() ? it.Current : 0;
            y = it.MoveNext() ? it.Current : 0;
            z = it.MoveNext() ? it.Current : 0;
        }
        
        public readonly Vec3I Abs() => new(Math.Abs(x), Math.Abs(y), Math.Abs(z));
        public readonly IEnumerator<int> GetEnumerator()
        {
            yield return x;
            yield return y;
            yield return z;
        }

        readonly IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
        public override readonly string ToString() => $"({x},{y},{z})";

        public readonly bool Equals(Vec3I rhs) => rhs.Zip(this).Aggregate(true, (isEq, v) => isEq && v.First == v.Second);
        public override readonly int GetHashCode() => HashCode.Combine(x, y, z);
        public override readonly bool Equals(object obj) => obj is Vec3I v && Equals(v);

        public static Vec3I Zero => new(0, 0, 0);
        public static Vec3I One => new(1, 1, 1);

        public static Vec3I operator +(Vec3I lhs, Vec3I rhs) => new(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z);
        public static Vec3I operator -(Vec3I lhs, Vec3I rhs) => new(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z);

        public static bool operator ==(Vec3I lhs, Vec3I rhs) => lhs.Equals(rhs);
        public static bool operator !=(Vec3I lhs, Vec3I rhs) => !lhs.Equals(rhs);
#if GODOT4
        public static implicit operator Godot.Vector3I(Vec3I v) => new(v.x, v.y, v.z);
        public static implicit operator Vec3I(Godot.Vector3I v) => new(v.X, v.Y, v.Z);
#endif
    }
}